﻿using System;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Kaleida.ServiceMonitor.Model.StructuredSource;

namespace Kaleida.ServiceMonitor.Model.Runtime
{
    public class ScriptCompiler
    {
        private static readonly ReflectionOperationFactory defaultOperationFactory = new ReflectionOperationFactory(new[] { Assembly.GetExecutingAssembly() });

        private readonly IPreparedRequestFactory preparedRequestFactory ;
        private readonly IResponseHandlerFactory responseHandlerFactory ;

        public ScriptCompiler() : this(defaultOperationFactory, defaultOperationFactory)
        {
        }

        public ScriptCompiler([NotNull] IPreparedRequestFactory preparedRequestFactory, [NotNull] IResponseHandlerFactory responseHandlerFactory)
        {
            if (preparedRequestFactory == null) throw new ArgumentNullException("preparedRequestFactory");
            if (responseHandlerFactory == null) throw new ArgumentNullException("responseHandlerFactory");

            this.preparedRequestFactory = preparedRequestFactory;
            this.responseHandlerFactory = responseHandlerFactory;
        }

        public CompiledScript Build(ScriptDefinition definition)
        {
            var script = new CompiledScript();

            foreach(var operation in definition.Operations)
            {
                try
                {
                    var preparedRequest = preparedRequestFactory.Build(operation.RequestAction.Name, operation.RequestAction.Arguments);
                    var responseHandler = responseHandlerFactory.Build(operation.HandlerAction.Name, operation.HandlerAction.Arguments);
                    script.Operations.Add(new Operation(preparedRequest, responseHandler));
                }
                catch (Exception exception)
                {
                    throw new InvalidOperationException(string.Format("Could not build operation {0} of {1}: {2}", definition.Operations.IndexOf(operation), definition.Operations.Count(), exception.Message));
                }
            }

            var processor = new DirectiveProcessor(script);
            processor.ApplyToScript(definition.Directives);
            

            return script;
        }
    }
}
